package cn.ronghuanet.auth.service.impl;

import cn.ronghuanet.auth.annotation.RonghuaPermission;
import cn.ronghuanet.auth.domain.Permission;
import cn.ronghuanet.auth.mapper.PermissionMapper;
import cn.ronghuanet.auth.service.IPermissionScanService;
import cn.ronghuanet.basic.enums.RequestTypeEnum;
import cn.ronghuanet.basic.utils.ClassUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Objects;

@Service
public class PermissionScanServiceImpl implements IPermissionScanService {

    @Value("${ronghua.permission.scan-base-package}")
    private String scanBasePackage;

    @Autowired
    private PermissionMapper permissionMapper;

    /**
     * 权限扫描
     */
    @Transactional
    @Override
    public void scan() {
        // 1 拿到所有的controller类,自定义注解只会用在controller类和它的方法上面
        List<Class> classes = ClassUtils.getAllClassName(this.scanBasePackage);
        System.out.println(classes);

        // 每次启动都会来扫描权限,并添加到数据库,就会存在着相同权限在数据库里面保存多份的情况,怎么解决?
        // 方案一:每次保存时,通过权限的sn(唯一标识)判断当前权限在数据库是否存在,如果存在就修改,如果不存在就新增
        // 方案一要思考一个问题,第一次的时候方法上有权限,但是后续因为业务需求,方法不需要权限了,方法上的注解就删除了,这种情况是应该将原权限删掉的
        // 方案二: 先全部删除,再重新新增(采纳这种方案)
        permissionMapper.deleteAll();   // 删除所有不能放在新增的后面

        for (Class aClass : classes) {
            // 2 判断controller类上是否有@RonghuaPermission
            RonghuaPermission classAnnotation = (RonghuaPermission)aClass.getAnnotation(RonghuaPermission.class);
            if(Objects.isNull(classAnnotation)){
                // 3 如果没有@RonghuaPermission,说明该类里面的方法都不需要权限,直接跳过不处理
                continue;
            }

            RequestMapping requestMapping = (RequestMapping)aClass.getAnnotation(RequestMapping.class);
            String classUrl = requestMapping.value()[0];
            // 将类上面的注解也要解析为一个Permission对象,它是作为里面方法权限对象的父权限
            Permission parent = new Permission();
            parent.setName(classAnnotation.name());
            parent.setDescs(classAnnotation.descs());
            parent.setUrl(classUrl);        // 一级权限的url,就是类上面的那个url
            parent.setSn(aClass.getSimpleName());   // 一级权限的唯一标识,就是类名
            // 保存后拿到父权限的id
            permissionMapper.insert(parent);

            // 4 如果有@RonghuaPermission,拿到类里面所有的方法,只能拿到类里面用public修饰的方法
            Method[] methods = aClass.getMethods();

            // 5 循环所有的方法,判断方法上面是否有@RonghuaPermission
            for (Method method : methods) {
                RonghuaPermission methodAnnotation = method.getAnnotation(RonghuaPermission.class);
                if(Objects.isNull(methodAnnotation)){
                    // 6 如果方法上面没有注解,说明当前方法不需要权限,直接跳过不处理
                    continue;
                }
                // 7 如果方法上面有注解,就要解析@RonghuaPermission,封装Permission对象
                Permission permission = new Permission();
                permission.setName(methodAnnotation.name());
                permission.setDescs(methodAnnotation.descs());
                // 处理url 类上面的url+方法上面的url
                String methodUrl = getMethodUrl(method);
                permission.setUrl(classUrl+methodUrl);
                // 处理sn 唯一标识  类名:方法名
                permission.setSn(aClass.getSimpleName()+":"+method.getName());
                // 处理父权限ID
                permission.setParentId(parent.getId()); // 以父权限的ID作为子权限的父ID

                // 8 封装的Permission对象保存到数据库
                permissionMapper.insert(permission);
            }
        }

    }

    private String getMethodUrl(Method method){
        String methodUrl = "";
        // 取枚举中定义的所有的请求类型
        RequestTypeEnum[] requestTypeEnums = RequestTypeEnum.values();
        for (RequestTypeEnum typeEnum : requestTypeEnums) {
            // 根据枚举中的不同的请求类型的class获取该类型对应的注解对象
            Annotation annotation = method.getAnnotation(typeEnum.getRequestType());
            // 如果该请求类型注解不存在就跳过
            if(Objects.isNull(annotation)){
                continue;
            }
            try {
                // 如果该请求类型注解存在,就获取它里面的value方法
                Method annotationMethod = annotation.annotationType().getMethod("value");
                // 通过invoke调用该对象的value方法,获取结果
                String[] value = (String[]) annotationMethod.invoke(annotation);
                // 如果value不为空且长度大于0,就赋值给methodUrl,并跳出循环
                if(value != null && value.length > 0){
                    methodUrl = value[0];
                    break;
                }
            }catch (Exception e) {
                e.printStackTrace();
                continue;
            }
        }
        return methodUrl;
        /*PutMapping putMapping = method.getAnnotation(PutMapping.class);
        if(Objects.nonNull(putMapping)){
            methodUrl = putMapping.value() != null && putMapping.value().length > 0 ? putMapping.value()[0] : "";
        }
        PostMapping postMapping = method.getAnnotation(PostMapping.class);
        if(Objects.nonNull(postMapping)){
            methodUrl = postMapping.value() != null && postMapping.value().length > 0 ? postMapping.value()[0] : "";
        }
        GetMapping getMapping = method.getAnnotation(GetMapping.class);
        if(Objects.nonNull(getMapping)){
            methodUrl = getMapping.value() != null && getMapping.value().length > 0? getMapping.value()[0] : "";
        }
        DeleteMapping deleteMapping = method.getAnnotation(DeleteMapping.class);
        if(Objects.nonNull(deleteMapping)){
            methodUrl = deleteMapping.value() != null && deleteMapping.value().length > 0 ? deleteMapping.value()[0] : "";
        }
        PatchMapping patchMapping = method.getAnnotation(PatchMapping.class);
        if(Objects.nonNull(patchMapping)){
            methodUrl = patchMapping.value() != null && patchMapping.value().length > 0 ? patchMapping.value()[0] : "";
        }*/
        //return methodUrl;
    }
}
